###############################################################################
#                                                                             #
#    This file is part of the "ssbot" Python module.                          #
#    It is distributed under the MIT License. See LICENSE.txt for details.    #
#                                                                             #
###############################################################################

import os
import subprocess
import threading
import time

import ssutils


class AbstractStep(object):
    def __init__(self, name, cmd, cwd, deps=None, timeout=None):
        ssutils.validate_name(name)
        self.name = name
        self.cmd = cmd
        self.cwd = cwd
        self.deps = deps
        self.timeout = timeout

    def run(self, base_path, options=None):
        raise NotImplementedError()


class SubprocessCommunicator(object):
    def __init__(self, proc):
        self.proc = proc
        self.stdout = None
        self.stderr = None
        self.retcode = None

    def __call__(self):
        self.stdout, self.stderr = self.proc.communicate()
        self.retcode = self.proc.returncode

    def kill(self):
        try:
            self.proc.kill()
            self.stdout = 'Timed out'
            self.stderr = 'Timed out'
            self.retcode = -1
        except OSError:
            # This means that the process finished in the meanwhile.
            pass


class Step(AbstractStep):
    def run(self, base_path, options=None):
        full_path = os.path.join(base_path, self.cwd)
        proc = subprocess.Popen(self.cmd,
                                stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                                cwd=full_path)
        sc = SubprocessCommunicator(proc)
        t = threading.Thread(target=sc)
        t.start()
        if self.timeout:
            t.join(self.timeout)
        else:
            t.join()
        if t.is_alive():
            sc.kill()
        return {
            'name': self.name,
            'cmd': self.cmd,
            'cwd': full_path,
            'retcode': sc.retcode,
            'stdout': sc.stdout,
            'stderr': sc.stderr
        }
